跳到主要内容

Java Netty 通道 Channel

Channel 的常用方法

close() 可以用来关闭Channel

closeFuture() 用来处理 Channel 的关闭

  • sync 方法作用是同步等待 Channel 关闭
  • 而 addListener 方法是异步等待 Channel 关闭

pipeline() 方法用于添加处理器

write() 方法将数据写入

  • 因为缓冲机制,数据被写入到 Channel 中以后,不会立即被发送
  • 只有当缓冲满了或者调用了flush()方法后,才会将数据通过 Channel 发送出去

writeAndFlush() 方法将数据写入并立即发送(刷出)

同步连接的问题

拆分客户端代码

public class MyClient {
public static void main(String[] args) throws IOException, InterruptedException {
ChannelFuture channelFuture = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
})
// 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是NIO线程
// NIO线程:NioEventLoop 中的线程
.connect(new InetSocketAddress("localhost", 8080));

// 该方法用于等待连接真正建立
channelFuture.sync();

// 获取客户端-服务器之间的Channel对象
Channel channel = channelFuture.channel();
channel.writeAndFlush("hello world");
System.in.read();
}
}

如果我们去掉 channelFuture.sync() 方法,会服务器无法收到 hello world

这是因为建立连接(connect)的过程是异步非阻塞的,若不通过 sync() 方法阻塞主线程,等待连接真正建立,这时通过 channelFuture.channel() 拿到的 Channel 对象,并不是真正与服务器建立好连接的 Channel,也就没法将信息正确的传输给服务器端

所以需要通过 channelFuture.sync() 方法,阻塞主线程,同步处理结果,等待连接真正建立好以后,再去获得 Channel 传递数据。使用该方法,获取 Channel 和发送数据的线程都是主线程

下面还有一种方法,用于异步获取建立连接后的 Channel 和发送数据,使得执行这些操作的线程是 NIO 线程(去执行 connect 操作的线程)

addListener 方法异步连接

通过这种方法可以在 NIO 线程中获取 Channel 并发送数据,而不是在主线程中执行这些操作

public class MyClient {
public static void main(String[] args) throws IOException, InterruptedException {
ChannelFuture channelFuture = new Bootstrap()
.group(new NioEventLoopGroup())
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
})
// 该方法为异步非阻塞方法,主线程调用后不会被阻塞,真正去执行连接操作的是 NIO 线程
// NIO线程:NioEventLoop 中的线程
.connect(new InetSocketAddress("localhost", 8080));

// 当 connect 方法执行完毕后,也就是连接真正建立后
// 会在 NIO 线程中调用 operationComplete 方法
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
Channel channel = channelFuture.channel();
channel.writeAndFlush("hello world");
}
});
System.in.read();
}
}

关闭 ChannelFuture

public class ReadClient {
public static void main(String[] args) throws InterruptedException {
// 创建EventLoopGroup,使用完毕后关闭
NioEventLoopGroup group = new NioEventLoopGroup();

ChannelFuture channelFuture = new Bootstrap()
.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new StringEncoder());
}
})
.connect(new InetSocketAddress("localhost", 8080));
channelFuture.sync();

Channel channel = channelFuture.channel();
Scanner scanner = new Scanner(System.in);

// 创建一个线程用于输入并向服务器发送
new Thread(()->{
while (true) {
String msg = scanner.next();
if ("q".equals(msg)) {
// 关闭操作是异步的,在NIO线程中执行
channel.close();
break;
}
channel.writeAndFlush(msg);
}
}, "inputThread").start();

// 获得closeFuture对象
ChannelFuture closeFuture = channel.closeFuture();
System.out.println("waiting close...");

// 同步等待NIO线程执行完close操作
closeFuture.sync();

// 关闭之后执行一些操作,可以保证执行的操作一定是在channel关闭以后执行的
System.out.println("关闭之后执行一些额外操作...");

// 关闭EventLoopGroup
group.shutdownGracefully();
}
}

关闭 channel 的时机

当我们要关闭 channel 时,可以调用 channel.close() 方法进行关闭。但是该方法也是一个异步方法。真正的关闭操作并不是在调用该方法的线程中执行的,而是在 NIO 线程中执行真正的关闭操作

如果我们想在 channel 真正关闭以后,执行一些额外的操作,可以选择以下两种方法来实现

1、通过 channel.closeFuture() 方法获得对应的 ChannelFuture 对象,然后调用 sync() 方法阻塞执行操作的线程,等待 channel 真正关闭后,再执行其他操作

// 获得closeFuture对象
ChannelFuture closeFuture = channel.closeFuture();

// 同步等待NIO线程执行完close操作
closeFuture.sync();

2、调用 closeFuture.addListener 方法,添加 close 的后续操作

closeFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
// 等待channel关闭后才执行的操作
System.out.println("关闭之后执行一些额外操作...");
// 关闭EventLoopGroup
group.shutdownGracefully();
}
});